Skip to content

fix TypeScript definitions for parseUrl, agentOptions and RequestError.request, and drop the undici import - #382

Merged
glynnbird merged 2 commits into
apache:mainfrom
klaemo:types/v11-configuration-and-errors
Oct 28, 2026
Merged

glynnbird merged 2 commits into
apache:mainfrom
klaemo:types/v11-configuration-and-errors

Conversation

@klaemo

@klaemo klaemo commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Overview

Fixes to lib/nano.d.ts only; there are no runtime changes. As agreed in #381, this combines #362, #380 and #381, because they touch the same lines.

  1. Configuration.parseUrl was missing (parseUrl option is missing from the Configuration type in v11 #362). The runtime still reads it ('parseUrl' in cfg), and the README documents it. The "Set to false to prevent parsing of url" comment had ended up above headers, with the "Custom request headers" comment after it. This adds parseUrl?: boolean back and puts both comments on the right properties.
  2. Configuration.agentOptions is now a Dispatcher instance (TypeScript: agentOptions and RequestError.request types don't match the v11 runtime #380). It used to be a union of AgentOptions and constructor types (typeof undici.Agent, ...). nano passes the value unchanged to fetch as dispatcher, so it has to be an instance. Before this change, new Agent({...}) and new MockAgent() failed with TS2322. Passing the Agent class or a plain options object typechecked, but every request then failed with TypeError: agent.dispatch is not a function. The AgentOptions interface stays exported, so existing nano.AgentOptions references keep compiling.
  3. RequestError.request now describes the object nano attaches (TypeScript: agentOptions and RequestError.request types don't match the v11 runtime #380). That object is the scrubbed fetchOptions from relax(): url (including the query string), lower-case method, headers, body, credentials, redirect, signal, bodyTimeout, and dispatcher when one is configured. This replaces the old uri/qs shape, which nano 11 never sets. All properties stay optional, like the rest of RequestError.
  4. No more undici import (TypeScript: published nano.d.ts imports undici, which is only a devDependency #381). This is option 2 from the issue. nano.d.ts declares a structural nano.Dispatcher interface (dispatch(options: object, handler: object): boolean) instead of import undici from "undici". Consumers no longer need undici's types to compile with skipLibCheck: false, and with skipLibCheck: true agentOptions is no longer silently any. undici remains a devDependency for the test mocks.

Compatibility: the only code that stops compiling is code that was already broken at runtime. That covers reading err.request.uri/err.request.qs (always undefined in v11) and passing a class, a plain object or a non-dispatcher as agentOptions. Comparisons like err.request?.method === 'GET' still compile, although at runtime the method is lower case.

Testing recommendations

npm test passes (tsc lib/nano.d.ts plus 243 tests). The repo has no type-level tests, so I checked the changes with consumer projects using module/moduleResolution: nodenext, strict and skipLibCheck: false.

With undici 7 installed, this file compiles cleanly with this PR as both ESM and CJS (import Nano = require('nano')). Against main it gives 7 × TS2322 (one per instance line), TS2353 on parseUrl, TS2339 on request.url, and 3 unused @ts-expect-error directives:

import Nano from 'nano'
import { Agent, MockAgent, Pool, ProxyAgent, RetryAgent, getGlobalDispatcher } from 'undici'

const url = 'http://localhost:5984'

// accepted: Dispatcher instances, parseUrl
Nano({ url, agentOptions: new Agent({ connections: 10 }) })
Nano({ url, agentOptions: new MockAgent() })
Nano({ url, agentOptions: new Pool(url) })
Nano({ url, agentOptions: new ProxyAgent('http://proxy:8080') })
Nano({ url, agentOptions: new RetryAgent(new Agent()) })
Nano({ url, agentOptions: getGlobalDispatcher() })
Nano({ url, agentOptions: new Agent().compose((d) => d) })
Nano({ url: `${url}/mydb`, parseUrl: false })

// rejected: these all throw "agent.dispatch is not a function" at runtime
// @ts-expect-error class constructor, not an instance
Nano({ url, agentOptions: Agent })
// @ts-expect-error plain options object
Nano({ url, agentOptions: { bodyTimeout: 1000 } })

// RequestError.request matches what nano attaches
const err = {} as Nano.RequestError
const reqUrl: string | undefined = err.request?.url
const method: string | undefined = err.request?.method
// @ts-expect-error not present at runtime; the query string is part of `url`
err.request?.qs
// @ts-expect-error not present at runtime; see `url` (`err.headers.uri` still exists)
err.request?.uri

Without undici installed, this compiles cleanly with this PR. Against main it fails with TS2307: Cannot find module 'undici' in nano.d.ts:

import Nano from 'nano'
Nano({ url: 'http://localhost:5984' })
// @ts-expect-error not a Dispatcher
Nano({ url: 'http://localhost:5984', agentOptions: 42 })

For reference, this is the runtime err.request for a 404 with an Agent configured. I got it against a local stub server with nano 11.0.7 on Node 24.21:

{
  url: 'http://localhost:39187/db/doc?revs_info=true',
  method: 'get',
  headers: { accept: 'application/json', 'user-agent': 'nano/11.0.7 (Node.js v24.21.0)',
             'Accept-Encoding': 'deflate, gzip', Authorization: 'XXXXXXX', 'content-type': 'application/json' },
  credentials: 'include',
  body: undefined,
  redirect: 'error',
  signal: undefined,
  bodyTimeout: 0,
  dispatcher: Agent { ... }
}

GitHub issue number

Fixes #362
Fixes #380
Fixes #381

Related Pull Requests

None. Separately, the README and migration guide examples pass the agent as undiciOptions, which nano ignores, rather than as agentOptions. That's a docs fix and not part of this PR.

Checklist

  • Code is written and works correctly;
  • Changes are covered by tests; (tsc lib/nano.d.ts in npm test, plus the consumer snippets above; the repo has no type-test harness)
  • Documentation reflects the changes; (JSDoc in nano.d.ts)

This PR was prepared with Claude Code (AI-assisted, per the ASF generative tooling guidance). I reviewed the changes and checked them against lib/nano.js.

🤖 Generated with Claude Code

klaemo and others added 2 commits October 28, 2026 12:52
…r.request. Fixes apache#362, fixes apache#380

- restore `parseUrl?: boolean` in `Configuration` and put the
  `headers` doc comment back above `headers`
- type `agentOptions` as an undici `Dispatcher` instance, which is what
  nano passes to `fetch` as `dispatcher`; the previous union of
  constructor types rejected `new Agent()` / `new MockAgent()` and
  accepted values that fail with "agent.dispatch is not a function"
- describe `RequestError.request` as the scrubbed fetch options nano
  attaches (`url`, lower-case `method`, `headers`, `body`, ...)
  instead of the request-era `uri`/`qs` shape

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e#381

undici is only a devDependency, so consumers without it got TS2307 from
`import ... from "undici"` (or an `any`-typed `agentOptions` with
skipLibCheck). Declare a structural `Dispatcher` interface instead; undici
Agent, Pool, MockAgent, ProxyAgent and RetryAgent instances all satisfy it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@glynnbird glynnbird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@glynnbird
glynnbird merged commit 0a3b91c into apache:main Oct 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants